#ifdef _WIN32
#include <windows.h>
#endif

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#include "m3u8_file.h"
#include "hlsparse.h"
#include "chain_url.h"


// 自定义的 _url_strdup 函数，接受两个 char* 参数
static char* url_strdup(char *original, const char *str) {
    // 如果原指针不为空，先释放原指针的内存
    if (original != NULL) {
        free(original);
    }

    // 如果传入的需复制的指针为空，返回 NULL
    if (str == NULL) {
        return NULL;
    }

    // 计算字符串长度，包含 '\0'
    size_t len = strlen(str) + 1;

    // 使用 calloc 分配内存，保证分配的内存被初始化为 0
    char *copy = (char *)calloc(len, sizeof(char));
    if (copy == NULL) {
        // 内存分配失败，返回 NULL
        return NULL;
    }

    // 将传入的字符串拷贝到新分配的内存
    memcpy(copy, str, len);

    return copy;
}

static char *gen_new_url(M3U8File* m3u8, const char *part_url)
{
    size_t base_len = strlen(m3u8->url_base);
    size_t part_len = strlen(part_url);
    char *new_url = calloc(1, base_len + part_len + 1); // 精确分配所需内存
    if (!new_url) {
        fprintf(stderr, "Failed to allocate memory for new_url\n");
        return NULL;
    }
    strcpy(new_url, m3u8->url_base);
    strcat(new_url, part_url);
    return new_url;
}

static HLSCode parserPl(M3U8File* m3u8, const char *src)
{
    // parse the playlist information into our master structure
    media_playlist_t pl;
    hlsparse_media_playlist_init(&pl);
    HLSCode res = hlsparse_media_playlist(src, strlen(src), &pl);
    printf("read a total of %d bytes parsing the media playlist source\n", res);
    printf("num of segments: %d\n", pl.nb_segments);
    segment_list_t *list = &pl.segments;

    // parse if repeated
    int index = 0;
    int repeat_index = -1;
    if(m3u8->url_last_pl)
    {
        while(list){
            segment_t* seg = list->data;
            char *new_url = gen_new_url(m3u8, seg->uri);
            if(strcmp(new_url, m3u8->url_last_pl) == 0){
                repeat_index = index;
            }
            free(new_url);
            list = list->next;
            index++;
            if (repeat_index >= 0) {
                break;
            }
        }
    }

    index = 0;
    list = &pl.segments;
    while(list){
        if(index > repeat_index){
            segment_t* seg = list->data;
            char *new_url = gen_new_url(m3u8, seg->uri);
            printf("seg->duration: %f\n", seg->duration);
            printf("seg->uri: %s\n", seg->uri);
            chain_url_add(&m3u8->pM3U8->pl, new_url);
            free(new_url);
        }
        list = list->next;
        index++;
    }
    hlsparse_media_playlist_term(&pl);
    return HLS_OK;
}

static HLSCode parserPlFromMaster(M3U8File* m3u8, const char *part_url)
{
    char *sec_url = gen_new_url(m3u8, part_url);
    if (!sec_url) {
        return HLS_ERROR; // 内存分配失败
    }

    NetData *sec_data = net_fread_all(sec_url);
    if (!sec_data) {
        fprintf(stderr, "Failed to read file '%s'\n", sec_url);
        free(sec_url);  // 确保释放内存
        return HLS_ERROR;
    }

    HLSCode res = parserPl(m3u8, sec_data->data);
    net_fread_all_free(sec_data);
    free(sec_url);
    return res;
}


static HLSCode parserMsater(M3U8File* m3u8, int *is_master)
{
    // create a multivariant playlist structure
    multivariant_playlist_t myMultivariant;
    HLSCode res = hlsparse_multivariant_playlist_init(&myMultivariant);
    if (res != HLS_OK) {
        fprintf(stderr, "failed to initialize multivariant playlist structure");
        return -1;
    }

    *is_master = 0;

    int read = hlsparse_multivariant_playlist(m3u8->pM3U8->content->data, strlen(m3u8->pM3U8->content->data), &myMultivariant);
    printf("read a total of %d bytes parsing the master playlist source\n", read);
    printf("num of streams: %d, num of iframe_streams, %d, num of keys: %d\n",
           myMultivariant.nb_stream_infs, myMultivariant.nb_iframe_stream_infs, myMultivariant.nb_session_keys);
    if(myMultivariant.nb_stream_infs > 0){
        *is_master = 1;

        stream_inf_list_t *streamInf = &myMultivariant.stream_infs;
        int count = 0;
        while (streamInf && streamInf->data) {
            // 发现是 master，需要进一步获取pl
            parserPlFromMaster(m3u8, streamInf->data->uri);

            printf("StreamInf %d Uri: %s\n", count, streamInf->data->uri);
            printf("StreamInf %d Bandwidth: %f\n", count, streamInf->data->bandwidth);
            printf("StreamInf %d Codec: %s\n", count, streamInf->data->codecs);
            printf("StreamInf %d Audio: %s\n", count, streamInf->data->audio);
            printf("StreamInf %d Video: %s\n", count, streamInf->data->video);
            printf("StreamInf %d WxH: %dx%d\n", count, streamInf->data->resolution.width, streamInf->data->resolution.height);
            ++count;
            streamInf = streamInf->next;
        }
    }
    printf("==========================================================\n");
#if PRINT_ALL_MEDIA_AND_SESS_DATA
    media_list_t *mediaInf = &myMultivariant.media;
    count = 0;
    while (mediaInf && mediaInf->data) {
        printf("Media %d Uri: %s\n", count, mediaInf->data->uri);
        ++count;
        mediaInf = mediaInf->next;
    }

    session_data_list_t *sessInf= &myMultivariant.session_data;
    count = 0;
    while (sessInf && sessInf->data) {
        printf("Sess %d Uri: %s\n", count, sessInf->data->uri);
        ++count;
        sessInf = sessInf->next;
    }
#endif
    // 销毁master结构
    hlsparse_multivariant_playlist_term(&myMultivariant);
    return HLS_OK;
}


static HLSCode parserM3U8(M3U8File* m3u8)
{
    HLSCode res = HLS_OK;
    int is_master = 0;
    res = parserMsater(m3u8, &is_master);
    if(is_master == 0){
        res = parserPl(m3u8, m3u8->pM3U8->content->data);
    }
    if(m3u8->pM3U8->pl){
        m3u8->pM3U8->pl_current = m3u8->pM3U8->pl;
    }
    return res;
}

static void unloadRootM3U8(M3U8File* m3u8)
{
    if (m3u8->pM3U8) {
        net_fread_all_free(m3u8->pM3U8->content);
        chain_url_destroy(&m3u8->pM3U8->pl);
        free(m3u8->pM3U8);
        m3u8->pM3U8 = NULL;  // 确保不会双重释放
    }
}

static void reloadRootM3U8(M3U8File* m3u8)
{
    // 重新加载根M3U8文件
    if(m3u8->pM3U8){
        unloadRootM3U8(m3u8);
    }
    m3u8->pM3U8 = calloc(1, sizeof(m3u8_t));
    if (!m3u8->pM3U8) return;

    m3u8->pM3U8->content = net_fread_all(m3u8->url);
    if (!m3u8->pM3U8->content) {
        fprintf(stderr, "Failed to read file '%s'\n", m3u8->url);
        return;
    }
    //printf("m3u8 file content:\n%s", m3u8->pM3U8->content->data);
    printf("==========================================================\n");        

    if (m3u8->pM3U8->content->data) {
        parserM3U8(m3u8);

        // 打印播放列表，方便调试
        chain_url_t* current = m3u8->pM3U8->pl;
        while (current != NULL) {    
            printf("-------> playlist: %s\n", current->url);
            current = current->next;
        }
    }
}

static size_t readFromTsBuffer(void* buffer, size_t size, size_t count, M3U8File* m3u8File)
{
    size_t totalBytesRead = 0;

    // 第一步，查看缓存数据还有多少，根据需求给返回。
    if(m3u8File->currentTsBuffer){
        size_t remain = m3u8File->currentTsBuffer->size - m3u8File->currentTsBuffer->offset;
        if(remain > size * count){
            totalBytesRead = size * count;
        }else{
            totalBytesRead = remain;
        }
        memcpy(buffer, m3u8File->currentTsBuffer->data + m3u8File->currentTsBuffer->offset, totalBytesRead);
        m3u8File->currentTsBuffer->offset += totalBytesRead;
        if(m3u8File->currentTsBuffer->offset >= m3u8File->currentTsBuffer->size){
            net_fread_all_free(m3u8File->currentTsBuffer);
            m3u8File->currentTsBuffer = NULL;
        }
    }
    return totalBytesRead / size;
}

// 打开M3U8文件
M3U8File* m3u8_fopen(const char* url) {
    M3U8File* handle = 0;

    // Initialize the library
    HLSCode res = hlsparse_global_init();
    if (res != HLS_OK) {
        fprintf(stderr, "failed to initialize hlsparse");
        return 0;
    }

    handle = calloc(1, sizeof(M3U8File));
    if (!handle) return NULL;

    // Init baseURL, 提取基础URL（去掉最后的文件名部分）
    handle->url = calloc(1, strlen(url) + 1);
    if (!handle->url) {
        free(handle);
        return NULL;
    }
    strcpy(handle->url, url);
    
    handle->url_base = calloc(1, strlen(url) + 1);
    if (!handle->url_base) {
        free(handle->url);
        free(handle);
        return NULL;
    }
    strcpy(handle->url_base, url);

    char* lastSlash = strrchr(handle->url_base, '/');
    if (lastSlash) {
        *(lastSlash + 1) = '\0'; // 保留最后一个斜杠，用于拼接相对路径
    }

    reloadRootM3U8(handle);

    return handle;
}

// 关闭M3U8文件并释放资源
void m3u8_fclose(M3U8File* m3u8File) {
    if (!m3u8File) return;

    unloadRootM3U8(m3u8File);
    free(m3u8File->url);
    free(m3u8File->url_base);
    if(m3u8File->url_last_pl){
        free(m3u8File->url_last_pl);
    }
    if(m3u8File->currentTsBuffer){
        net_fread_all_free(m3u8File->currentTsBuffer);
    }
    free(m3u8File);
}

// 从M3U8文件中读取数据
size_t m3u8_fread(void* buffer, size_t size, size_t count, M3U8File* m3u8File) {
    if (!m3u8File || !buffer) return 0;

    size_t totalBytesRead = 0;

    // 第一步，查看缓存数据还有多少，根据需求给返回。
    if(m3u8File->currentTsBuffer){
        return readFromTsBuffer(buffer, size, count, m3u8File);
    }

    // 第二步，判断是否播放列表已经全部读完，需要加载新的播放列表
    while(m3u8File->pM3U8->pl_current == NULL){
        reloadRootM3U8(m3u8File);
        if(m3u8File->pM3U8->pl_current == NULL){
            printf("read too fast, need wait...\r\n");
            Sleep(1000);
        }
    }

    // 第三步，当前播放列表还没读完，需要加载数据
    if(m3u8File->pM3U8->pl_current)
    {
        m3u8File->currentTsBuffer = net_fread_all(m3u8File->pM3U8->pl_current->url);
        if (m3u8File->currentTsBuffer) {
            m3u8File->currentTsBuffer->offset = 0;
            m3u8File->url_last_pl = url_strdup(m3u8File->url_last_pl, m3u8File->pM3U8->pl_current->url);
            m3u8File->pM3U8->pl_current = m3u8File->pM3U8->pl_current->next;
            return readFromTsBuffer(buffer, size, count, m3u8File);
        }
    }

    return totalBytesRead / size;
}

// 定位M3U8文件中的位置
int m3u8_fseek(M3U8File* m3u8File, long offset, int origin) {
    // 目前暂时不支持m3u8_fseek功能
    // 如果需要实现，请根据变体流和TS段的位置计算实际偏移
    return -1; // 返回-1表示不支持
}
